﻿using System;
using System.Collections.Generic;
using System.Linq;

using mvcForum.Core;
using mvcForum.Core.Events;
using mvcForum.Core.Interfaces.Services;
using mvcForum.Web.Helpers;
using mvcForum.Core.Interfaces.Data;
using mvcForum.Core.Interfaces.Events;

namespace mvcForum.Web.Services {

	public class TopicService : ITopicService {
		private readonly IContext context;
		private readonly IRepository<Topic> topicRepo;
		private readonly IRepository<Post> postRepo;
		private readonly IForumAccessService accessService;
		private readonly IEventPublisher eventPublisher;

		public TopicService(IContext context, IForumAccessService accessService, IEventPublisher eventPublisher) {
			this.context = context;
			this.accessService = accessService;
			this.eventPublisher = eventPublisher;
			this.topicRepo = this.context.GetRepository<Topic>();
			this.postRepo = this.context.GetRepository<Post>();
		}

		public Topic Create(ForumUser author, Forum forum, String subject, TopicType type, String body, String authorIP, String userAgent, String forumUrl, List<String> feedbackOutput) {
			Topic topic = null;

			AccessFlag flag = this.accessService.GetAccessFlag(forum, author);
			// Does the user have post access to the forum?
			if ((flag & AccessFlag.Post) == AccessFlag.Post) {
				topic = new Topic(author, forum, subject.Replace("<", "&gt;"));

				// Moderator?
				if ((flag & AccessFlag.Moderator) == AccessFlag.Moderator && (type == TopicType.Announcement | type == TopicType.Sticky)) {
					// Announcement??
					if (type == TopicType.Announcement) {
						topic.Type = TopicType.Announcement;
					}
					// or sticky??
					else if (type == TopicType.Sticky) {
						topic.Type = TopicType.Sticky;
					}
				}

				this.topicRepo.Create(topic);
				this.context.SaveChanges();

				Post post = new Post(author, topic, topic.Title, body, authorIP);

				this.postRepo.Create(post);
				this.context.SaveChanges();

				NewTopicEvent e = new NewTopicEvent { TopicId = topic.Id, ForumId = topic.Forum.Id, UserAgent = userAgent, ForumRelativeURL = forumUrl };
				this.eventPublisher.Publish(e);

				IEnumerable<EventFeedback> feedback = e.GetFeedback();
				if (feedback.Any()) {
					foreach (EventFeedback fb in feedback) {
						feedbackOutput.Add(ForumHelper.GetHtmlString(fb.TextKey, fb.Namespace, fb.Arguments).ToString());
					}
				}
			}

			return topic;
		}

		public Topic Read(ForumUser user, Int32 id) {
			Topic topic = this.topicRepo.Read(id);

			AccessFlag flag = this.accessService.GetAccessFlag(topic.Forum, user);
			// Does the user have post access to the forum?
			if ((flag & AccessFlag.Read) == AccessFlag.Read) {
				return topic;
			}

			return null;
		}

		public Boolean Update(ForumUser user, Topic topic, String title, String body, String forumURL) {
			return this.Update(user, topic, title, body, topic.Type, topic.Flag, String.Empty, forumURL);
		}

		public Boolean Update(ForumUser user, Topic topic, String title, String body, TopicType type, TopicFlag topicFlag, String reason, String forumURL) {
			Post post = topic.Posts.OrderBy(p => p.Position).First();
			TopicFlag originalFlag = topic.Flag;

			// What kind of access does the user have on this forum? (given by the topic!)
			AccessFlag flag = this.accessService.GetAccessFlag(topic.Forum, user);

			// Is this the author or a moderator?
			if (((flag & AccessFlag.Edit) == AccessFlag.Edit && user.Id == topic.Author.Id) ||
				(flag & AccessFlag.Moderator) == AccessFlag.Moderator) {

				// Let's change the title on the topic!
				topic.Title = title;
				// And update the content on the post!
				post.Update(user, title, body);

				// Not the author? Then we have a moderator!
				if (user.Id != topic.Author.Id || (flag & AccessFlag.Moderator) != 0) {
					// Set the topic type!
					topic.Type = type;
					//topic.RemoveFlag(topic.Flag);
					topic.SetFlag(topicFlag);
					if ((topicFlag & TopicFlag.Deleted) == TopicFlag.Deleted) {
						post.DeleteReason = reason;
					}
					else {
						post.Update(user, title, body, reason);
					}

					if (originalFlag != topic.Flag) {
						this.eventPublisher.Publish(new TopicFlagUpdatedEvent { TopicId = topic.Id, OriginalFlag = originalFlag, ForumRelativeURL = forumURL });
					}
				}

				this.context.SaveChanges();

				return true;
			}
			return false;
		}
	}
}